/*
 * Utra.cpp
 *
 *  Created on: 2012-12-24
 *      Author: lijian
 */

#include "Utra.h"
#include <fcntl.h>
#include <unistd.h>
#include <cstdlib>
#include <cstdio>
#include <stdexcept>
#include <sys/errno.h>
#include "CharChangeUtil.h"
#include <string.h>

#ifdef linux
#include <linux/major.h>
#include <linux/kdev_t.h>
#else
#define MAJOR(dev)	((dev)>>24)
#define MINOR(dev)	((dev) & 0xff)
#endif

void dir_file_list(string path, vector<string> &file_vec, bool iteration) {
	DIR *dp;
	struct dirent *dirp;
	if ((dp = opendir(path.c_str())) == NULL) {
		perror(path.c_str());
		free(dp);
		return;
	}

	while ((dirp = readdir(dp)) != NULL) {
		if ((strcmp(dirp->d_name, ".") == 0)
				|| (strcmp(dirp->d_name, "..") == 0))
			continue;
		string file_name = dirp->d_name;
		if (file_name.find_last_of(".swp") == file_name.size() - 1)
			continue;

		file_name = path + "/" + file_name;

		if (iteration) {
			struct stat buf;
			stat(file_name.c_str(), &buf);
			if (S_ISDIR(buf.st_mode)) {
				dir_file_list(string(file_name), file_vec, iteration);
			} else {
				file_vec.push_back(file_name);
			}
		} else {
			file_vec.push_back(file_name);
		}
	}
	closedir(dp);
}

Uart::Uart() {
	fd=0;
    sendSum=0;
    recvSum=0;
}

Uart::~Uart() {

}

void Uart::setConfig(string port, unsigned int buadRate, unsigned char dataBits,
		unsigned char stopBits, unsigned char parity,
		unsigned char flowControl) {
//	this->port=port;
//	this->buadRate=buadRate;
//	this->dataBits=dataBits;
//	this->stopBits=stopBits;
//	this->parity=parity;
//	this->flowControl=flowControl;
}

void Uart::setConfig(UtraConfig config) {
    this->config=config;
}

void Uart::open() {
	fd = ::open(config.port.c_str(), O_RDWR | O_NOCTTY | O_NDELAY);
	if (-1 == fd) {
		runtime_error e("unable open port! "+config.port+"\n"+string(strerror(errno)));
		throw e;
	}

	if (fcntl(fd, F_SETFL, 0) < 0) {
		runtime_error e("fcntl failed!");
		throw e;
	}

	if (isatty(fd) == 0) {
		runtime_error e("standard input is not a terminal device!");
		throw e;
	}
    fcntl(fd,F_SETFL,0);
    this->setOpt();
}

vector<string> Uart::get_all_utras() {
	vector<string> utras,files;
	dir_file_list("/dev",files,false);
	struct stat m_stat;
	for (vector<string>::size_type i = 0; i < files.size(); i++) {
        
        if(files[i].find("tty")==string::npos){
            continue;
        }

		int rtn=stat(files[i].c_str(),&m_stat);
		if(rtn==0){
			int major=MAJOR(m_stat.st_rdev);
//			cout<<files[i]<<" major:"<<major<<endl;

#ifdef linux
			if(major==188){
				utras.push_back(files[i]);
			}
#else
			if(major==33){
				utras.push_back(files[i]);
			}
#endif

		}else{
			perror(files[i].c_str());
		}
	}
	return utras;
}

int Uart::sendString(string str) {
    const char* cs=str.c_str();
    return this->write((void*)cs, (int)str.length());
}

int Uart::sendHex(string str) {
    unsigned char buff[1024];
    int size=0;
    CharChangeUtil::stringToHex(str, buff, &size);
    this->write(buff, size);
    return size;
}

void Uart::setOpt() {
	struct termios newtio, oldtio;
	if (tcgetattr(fd, &oldtio) != 0) {
		runtime_error e("SetupSerial 1");
		throw e;
	}
	bzero( &newtio, sizeof( newtio ) );
	newtio.c_cflag |= CLOCAL | CREAD;
	newtio.c_cflag &= ~CSIZE;

	switch (config.dataBits) {
	case 7:
		newtio.c_cflag |= CS7;
		break;
	case 8:
		newtio.c_cflag |= CS8;
		break;
	}

	switch (config.parity) {
	case Odd:
		newtio.c_cflag |= PARENB;
		newtio.c_cflag |= PARODD;
		newtio.c_iflag |= (INPCK | ISTRIP);
		break;
	case Even:
		newtio.c_iflag |= (INPCK | ISTRIP);
		newtio.c_cflag |= PARENB;
		newtio.c_cflag &= ~PARODD;
		break;
	case None:
		newtio.c_cflag &= ~PARENB;
		break;
	}

	cfsetispeed(&newtio, config.buadRate);
	cfsetospeed(&newtio, config.buadRate);

//	switch (config.buadRate) {
//	case 2400:
//		cfsetispeed(&newtio, B2400);
//		cfsetospeed(&newtio, B2400);
//		break;
//	case 4800:
//		cfsetispeed(&newtio, B4800);
//		cfsetospeed(&newtio, B4800);
//		break;
//	case 9600:
//		cfsetispeed(&newtio, B9600);
//		cfsetospeed(&newtio, B9600);
//		break;
//	case 115200:
//		cfsetispeed(&newtio, B115200);
//		cfsetospeed(&newtio, B115200);
//		break;
//	default:
//		cfsetispeed(&newtio, B9600);
//		cfsetospeed(&newtio, B9600);
//		break;
//	}

	if (config.stopBits == 1)
		newtio.c_cflag &= ~CSTOPB;
	else if (config.stopBits == 2)
		newtio.c_cflag |= CSTOPB;
	newtio.c_cc[VTIME] = 1;
	newtio.c_cc[VMIN] = 0;
	tcflush(fd, TCIFLUSH);
	if ((tcsetattr(fd, TCSANOW, &newtio)) != 0) {
		runtime_error e("com set error");
		throw e;
	}
	newtio.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); /*Input*/
	newtio.c_oflag  &= ~OPOST;   /*Output*/
}

bool Uart::readData(unsigned char *buff , int *size){
    int len=this->read();
    if(len>0){
        if(buff!=NULL){
            memcpy(buff, &this->frame, len);
        }
        *size=len;
        return true;
    }else{
        return false;
    }
}

int Uart::write(void* buff, int len) {
    int ret = 0;
	ret = (int)::write(fd, buff, len);
    if(ret<0){
        runtime_error e(strerror(errno));
        throw e;
    }
	sendSum += ret;
	return ret;
}

int Uart::read(){
    int len = 0;
	len = (int)::read(fd, frame, 1024);
	if (len < 0) {
		runtime_error e(strerror(errno));
        throw e;
	}
	recvSum += len;
    lastFrameLen=len;
	return len;
}

void Uart::close(){
    if(fd!=0){
        ::close(fd);
        fd=0;
    }
}
